package com.meme.io.helper;

import java.io.RandomAccessFile;
import java.util.Stack;

import org.apache.log4j.Logger;

import com.meme.util.FileUtil;


class DataBlockSizeWriter
{
	static Logger logger = Logger.getLogger (DataBlockSizeWriter.class);
	
	RandomAccessFile mRandomAccessFile;
	
	long mSizeValueFilePointer;
	long mDataBeginFilePointer;
	
	public DataBlockSizeWriter (RandomAccessFile randomAccessFile)
	{
		mRandomAccessFile = randomAccessFile;
	}
	
	public void beginDataBlock () throws Exception
	{
		mSizeValueFilePointer = mRandomAccessFile.getFilePointer();
		mRandomAccessFile.writeInt (0); // occupy position
		mDataBeginFilePointer = mRandomAccessFile.getFilePointer();
	}
	
	public void endDataBlcok () throws Exception
	{
        long currentPointer = mRandomAccessFile.getFilePointer();
        mRandomAccessFile.seek (mSizeValueFilePointer);
        mRandomAccessFile.writeInt((int)(currentPointer - mDataBeginFilePointer));
        mRandomAccessFile.seek(currentPointer);
	}
}

class DataBlockOffsetsWriter
{
	static Logger logger = Logger.getLogger (DataBlockOffsetsWriter.class);
	
	RandomAccessFile mRandomAccessFile;
	int mDataBlocksCount;
	
	long mTableBeginFilePointer;
	
	public DataBlockOffsetsWriter (RandomAccessFile randomAccessFile)
	{
		mRandomAccessFile = randomAccessFile;
		mDataBlocksCount = -1;
	}
	
	public void beginDataBlocks (int blocksCount) throws Exception
	{
		logger.assertLog (mDataBlocksCount < 0, "mDataBlocksCount should be less than 0 now.");
		
		mDataBlocksCount = blocksCount;
		mCurrentBlockID = 0;
		
		mRandomAccessFile.writeInt (blocksCount);
		
		mTableBeginFilePointer = mRandomAccessFile.getFilePointer();
		
		while (blocksCount -- > 0) // (blocksCount) * (int + int)
		{
			mRandomAccessFile.writeInt (0); // begin
			mRandomAccessFile.writeInt (0); // end
		}
	}
	
	public void endDataBlocks () throws Exception
	{
		mDataBlocksCount = -1;
	}
	
	public void beginDataBlock (int blockID) throws Exception
	{
		logger.assertLog(blockID >= 0 && blockID < mDataBlocksCount, "blockID (" + blockID + ") is out of range.");
		
		long currentPointer = mRandomAccessFile.getFilePointer();
		mRandomAccessFile.seek (mTableBeginFilePointer + blockID * 8);
		mRandomAccessFile.writeInt ( (int)currentPointer );
		mRandomAccessFile.seek(currentPointer);
	}
	
	public void endDataBlock (int blockID) throws Exception
	{
		logger.assertLog(blockID >= 0 && blockID < mDataBlocksCount, "blockID (" + blockID + ") is out of range.");
		
		long currentPointer = mRandomAccessFile.getFilePointer();
		mRandomAccessFile.seek (mTableBeginFilePointer + blockID * 8 + 4);
		mRandomAccessFile.writeInt ( (int)currentPointer );
		mRandomAccessFile.seek(currentPointer);
	}
	
	int mCurrentBlockID;
	
	public void beginDataBlock () throws Exception
	{
		beginDataBlock (mCurrentBlockID);
	}

	public void endDataBlock () throws Exception
	{
		endDataBlock (mCurrentBlockID);
		++ mCurrentBlockID;
	}
}


public class BinaryFilePackager extends RandomAccessFile
{
	static Logger logger = Logger.getLogger (BinaryFilePackager.class);	


    public BinaryFilePackager(String filename) throws Exception
    {
        super ( preHandle (filename), "rw" );
    }
    
	static String preHandle (String filename)
	{
		FileUtil.deleteFile (filename);
		
		return filename;
	}


	public void writeFile( String inFilename ) throws Exception
    {
        RandomAccessFile inFile = new RandomAccessFile ( inFilename, "r" );
        byte[] buffer = new byte[ (int) inFile.length() ];
        int bytes = inFile.read( buffer );
        write(buffer, 0, bytes);
    }


    Stack <DataBlockSizeWriter> mDataBlockSizeWriterStack = new Stack <DataBlockSizeWriter> ();

    public void beginDataBlockWithSize () throws Exception
    {
    	DataBlockSizeWriter dataBlockSizeWriter = new DataBlockSizeWriter (this);
        mDataBlockSizeWriterStack.push ( dataBlockSizeWriter );
        dataBlockSizeWriter.beginDataBlock ();
    }

    public void endDataBlockWithSize () throws Exception
    {
    	DataBlockSizeWriter dataBlockSizeWriter = mDataBlockSizeWriterStack.pop();
    	
    	logger.assertLog(dataBlockSizeWriter != null, "mDataBlockSizeWriterStack is blank.");

    	dataBlockSizeWriter.endDataBlcok();
    }

    
    
    DataBlockOffsetsWriter mDataBlockOffsetsWriter   = null;

    public void beginDataBlcoksWithOffsetTable (int nBlocks) throws Exception
    {
    	if (mDataBlockOffsetsWriter != null)
    	{
    		logger.error ("mDataBlockOffsetsWriter should be null now.");
    		throw new Exception ("mDataBlockOffsetsWriter should be null now.");
    	}
 
    	mDataBlockOffsetsWriter = new DataBlockOffsetsWriter (this);
    	mDataBlockOffsetsWriter.beginDataBlocks (nBlocks);
    }
    
    public void endDataBlcoksWithOffsetTable () throws Exception
    {
    	mDataBlockOffsetsWriter.endDataBlocks();
    	mDataBlockOffsetsWriter = null;
    }

    public void beginDataBlock () throws Exception
    {
    	mDataBlockOffsetsWriter.beginDataBlock ();
    }

    public void endDataBlcok () throws Exception
    {
    	mDataBlockOffsetsWriter.endDataBlock ();
    }



}

